-
Notifications
You must be signed in to change notification settings - Fork 10.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-16121] Increasingly excessive memory requirements for linking on Linux #58380
Comments
Here is a build log from a user who was attempting to build on DigitalOcean’s App Platform: build_log_out_of_memory.txt |
Does using lld help this? (I don't think that's the default) |
Can you check what the size of the linker inputs is? If this is a regression, can you reproduce a working environment and compare the sizes? |
I managed to reproduce the memory usage. In my case, the link step would use 11Gb of memory. Finding the size of the inputs is not trivial given how hidden the build itself is. With some effort I extracted the link line. The linker is taking only one argument which is a response file. In the invocation that was created in my case, the response file contains over 18000 arguments, most of them object files and libraries. The link line has this structure:
It turns out the Manually deduplicating the response file, you can get it down to less than 2000 arguments. Using this new response file, it links way faster and used 500Mb of memory. It seems like SwiftPM should not add duplicate libraries inside of the |
interesting find, thanks allot @fredriss @abertelrud @neonichu thoughts? |
this may actually be happening in Driver and not SwiftPM, I think the relevant code is in Driver's
|
In the above code-block I can see a scenario where we expand this response file (to generate a different, top-level linker response file?) and its contents get us all of the duplicated |
Reproduced this with a Swift 5.6.1 toolchain on a test Vapor project on Ubuntu 20.04.
Which contains, for example, 1057 occurrences of
According to the log, this file is produced by an
But, the interesting part is that when I manually run this exact |
Ah, no, I misread the log. The
The inputs to this invocation are all object files involved in the build, of which there are 1927. And this is the invocation that consistently outputs an absurd output response file. My guess is that The solution here is to teach |
Would also be nice if we could teach the linker to deduplicate its inputs a bit. |
(We don't control the Linux linker, but even if we did:) Technically the linker cannot deduplicate. The order of inputs is significant and putting a library twice in non-consecutive spots has a certain meaning, even inside of a group. |
I know, but even given the absurdity of the input, the linker really ought to be able to do better than going pathological complexity in both space and time 😕 |
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
Otherwise we can duplicate linker flags across input binaries, which can result in very large linkerr invocation commands. Resolves swiftlang#58380
…d more than once A partial solution to swiftlang#58380
…d more than once A partial solution to swiftlang#58380
…d more than once A partial solution to swiftlang#58380
…d more than once A partial solution to swiftlang#58380
In my experiments #59115 reduces memory consumption when linking a sample Vapor app by ~2GB. This still leaves memory consumption to be quite high so this issue is worth keeping open to track further improvements. |
An update: I did some builds using both the 5.8 and ~/LinkTest$ docker run --pull -ti --privileged -e 'TERM=xterm-256color' \
--mount type=bind,source=$(pwd),target=/src -w/src \
swiftlang/swift:nightly-5.8-jammy
################################################################
# #
# Swift Nightly Docker Image #
# Tag: swift-5.8-DEVELOPMENT-SNAPSHOT-2023-02-23-a #
# #
################################################################
root@168d96cf79e1:/src# swift --version
Swift version 5.8-dev (LLVM 44d4f9d4b49845f, Swift b9562e1a860ec0b)
Target: aarch64-unknown-linux-gnu debug (
release (
I took the response file from the release build and manually deduplicated the
... for a total of 9,265 duplicates removed. I then re-ran the link command with the new response file. The resulting binary executable was identical in byte size, and examination of both versions with
So that's an order of magnitude increase in space efficiency (not to mention a 5.5x improvement in time efficiency), a nearly-equivalent decrease in IOPS, and no apparent drawbacks. It seems a foregone conclusion (at least to me) that it's worth seeing if making (I realize it may not be possible/safe to generically deduplicate platform libraries like |
also cc @MaxDesiatov @etcwilde @al45tair |
[5.8] CMake: fix missing `SWIFT_CONCURRENCY_GLOBAL_EXECUTOR` Explanation: Resolves issues with static linking on Linux Risk: Medium, affects Linux builds and top-level CMake declarations. Original PRs: #65795 and #64312 for `main`, #65824 and #64633 for `release/5.9` Reviewed by: @al45tair @drexin @etcwilde Resolves: some of the issues reported in #65097, also resolves #58380 Tests: Added in swiftlang/swift-integration-tests#118 `SWIFT_CONCURRENCY_GLOBAL_EXECUTOR` is defined in `stdlib/cmake/modules/StdlibOptions.cmake`, which is not included during the first pass of evaluation of the root `CMakeLists.txt`. It is available on subsequent evaluations after the value is stored in CMake cache. This led to subtle bugs, where `usr/lib/swift_static/linux/static-stdlib-args.lnk` didn't contain certain flags on clean toolchain builds, but did contain them in incremental builds. Not having these autolinking flags in toolchain builds leads to errors when statically linking executables on Linux. Additionally, since our trivial lit tests previously didn't link Dispatch statically, they didn't expose a bug where `%import-static-libdispatch` substitution had a missing value. To fix that I had to update `lit.cfg` and clean up some of the related path computations to infer a correct substitution value.
Environment
Description
With each subsequent release of Swift, more and more available RAM is required to link Swift executables on Linux. The addition of the recommended
--static-swift-stdlib
build flag on Linux (not to mention the accepted evolution proposal to make this the default, SE-0342) exacerbates the issue to the point where 4GB is not enough RAM to successfully link an unmodified Vapor template app (the following snippet assumes thevapor
Homebrew formula is installed):Note: Issue content cleaned up from original Jira import.
Detail from JIRA
md5: 05c2ceaa59cb789e6c5b9eabe39cb06a
The text was updated successfully, but these errors were encountered: